/**
 * Summit MMORPG Server Software
 * Copyright (c) 2008 Summit Server Team
 * See COPYING for license details.
 */

#include "StdAfx.h"

//DELETE FROM gameobject_spawns WHERE `Map` = 530 AND `entry` in (183104, 183411, 182301, 183412, 183413, 183414);

#define BANNER_RANGE 900
#define UPDATE_PERIOD 5000
#define CAPTURE_RATE 20

// Towers
enum Towers
{
    TOWER_1, // North-west
    TOWER_2, // North
    TOWER_3, // North-east
    TOWER_4, // South-east
    TOWER_5, // South
    TOWER_COUNT,
};

// Tower GameObject Ids
#define G_TOWER_1 183104
#define G_TOWER_2 183411
#define G_TOWER_3 183412
#define G_TOWER_4 183413
#define G_TOWER_5 183414

#define BLESSING_OF_AUCHINDOUND 33377

// Owners of the towers, used for save/restore
int32 TFg_towerOwners[TOWER_COUNT] = { -1, -1, -1, -1, -1 };

// global variables
uint32 TFg_hordeTowers = 0;
uint32 TFg_allianceTowers = 0;
int32 TFg_superiorTeam = -1;			// SUPERIORITY

// Fields to update visual view of the client map
static const uint32 g_hordeStateFields[5]		= {	WORLDSTATE_TEROKKAR_TOWER1_HORDE,	WORLDSTATE_TEROKKAR_TOWER2_HORDE,	WORLDSTATE_TEROKKAR_TOWER3_HORDE,	WORLDSTATE_TEROKKAR_TOWER4_HORDE,	WORLDSTATE_TEROKKAR_TOWER5_HORDE };
static const uint32 g_allianceStateFields[5]	= {	WORLDSTATE_TEROKKAR_TOWER1_ALLIANCE, WORLDSTATE_TEROKKAR_TOWER2_ALLIANCE, WORLDSTATE_TEROKKAR_TOWER3_ALLIANCE, WORLDSTATE_TEROKKAR_TOWER4_ALLIANCE, WORLDSTATE_TEROKKAR_TOWER5_ALLIANCE };
static const uint32 g_neutralStateFields[5]		= {	WORLDSTATE_TEROKKAR_TOWER1_NEUTRAL,	WORLDSTATE_TEROKKAR_TOWER2_NEUTRAL,	WORLDSTATE_TEROKKAR_TOWER3_NEUTRAL,	WORLDSTATE_TEROKKAR_TOWER4_NEUTRAL,	WORLDSTATE_TEROKKAR_TOWER5_NEUTRAL };

// updates clients visual counter, and adds the buffs to players if needed
HEARTHSTONE_INLINE void UpdateTowerCount(shared_ptr<MapMgr> mgr)
{
	mgr->GetStateManager().UpdateWorldState(WORLDSTATE_TEROKKAR_ALLIANCE_TOWERS_CONTROLLED, TFg_allianceTowers);
	mgr->GetStateManager().UpdateWorldState(WORLDSTATE_TEROKKAR_HORDE_TOWERS_CONTROLLED, TFg_hordeTowers);

	if(TFg_superiorTeam == 0 && TFg_allianceTowers != 5)
	{
		mgr->RemovePositiveAuraFromPlayers(0, BLESSING_OF_AUCHINDOUND);
		TFg_superiorTeam = -1;
	}

	if(TFg_superiorTeam == 1 && TFg_hordeTowers != 5)
	{
		mgr->RemovePositiveAuraFromPlayers(1, BLESSING_OF_AUCHINDOUND);
		TFg_superiorTeam = -1;
	}

	if(TFg_allianceTowers == 5 && TFg_superiorTeam != 0)
	{
		TFg_superiorTeam = 0;
		mgr->CastSpellOnPlayers(0, BLESSING_OF_AUCHINDOUND);
	}

	if(TFg_hordeTowers == 5 && TFg_superiorTeam != 1)
	{
		TFg_superiorTeam = 1;
		mgr->CastSpellOnPlayers(1, BLESSING_OF_AUCHINDOUND);
	}
}

enum BannerStatus
{
    BANNER_STATUS_NEUTRAL = 0,
    BANNER_STATUS_ALLIANCE = 1,
    BANNER_STATUS_HORDE = 2,
};

class TerokkarForestBannerAI : public GameObjectAIScript
{
		map<uint32, uint32> StoredPlayers;
		uint32 Status;
		const char* ControlPointName;
		uint32 towerid;
		uint32 m_bannerStatus;

	public:

		TerokkarForestBannerAI(GameObjectPointer go) : GameObjectAIScript(go)
		{
			m_bannerStatus = BANNER_STATUS_NEUTRAL;
			Status = 50;

			switch(go->GetEntry())
			{
				case G_TOWER_1:
					ControlPointName = "Tower 1";
					towerid = TOWER_1;
					break;

				case G_TOWER_2:
					ControlPointName = "Tower 2";
					towerid = TOWER_2;
					break;

				case G_TOWER_3:
					ControlPointName = "Tower 3";
					towerid = TOWER_3;
					break;

				case G_TOWER_4:
					ControlPointName = "Tower 4";
					towerid = TOWER_4;
					break;

				case G_TOWER_5:
					ControlPointName = "Tower 5";
					towerid = TOWER_5;
					break;

				default:
					ControlPointName = "Unknown";
					break;
			}
		}

		void AIUpdate()
		{
			uint32 plrcounts[2] = { 0, 0 };

			// details:
			//   loop through inrange players, for new ones, send the enable CP worldstate.
			//   the value of the map is a timestamp of the last update, to avoid cpu time wasted
			//   doing lookups of objects that have already been updated

			unordered_set<PlayerPointer>::iterator itr = _gameobject->GetInRangePlayerSetBegin();
			unordered_set<PlayerPointer>::iterator itrend = _gameobject->GetInRangePlayerSetEnd();
			map<uint32, uint32>::iterator it2, it3;
			uint32 timeptr = (uint32)UNIXTIME;
			bool in_range;
			bool is_valid;
			PlayerPointer plr;

			for(; itr != itrend; ++itr)
			{
				if(!(*itr)->IsPvPFlagged() || (*itr)->InStealth())
					is_valid = false;
				else
					is_valid = true;

				in_range = (_gameobject->GetDistance2dSq((*itr)) <= BANNER_RANGE) ? true : false;

				it2 = StoredPlayers.find((*itr)->GetLowGUID());
				if(it2 == StoredPlayers.end())
				{
					// new player :)
					if(in_range)
					{
						(*itr)->SendWorldStateUpdate(WORLDSTATE_TEROKKAR_PVP_CAPTURE_BAR_DISPLAY, 1);
						(*itr)->SendWorldStateUpdate(WORLDSTATE_TEROKKAR_PVP_CAPTURE_BAR_VALUE, Status);
						StoredPlayers.insert(make_pair((*itr)->GetLowGUID(), timeptr));

						if(is_valid)
							plrcounts[(*itr)->GetTeam()]++;
					}
				}
				else
				{
					// oldie
					if(!in_range)
					{
						(*itr)->SendWorldStateUpdate(WORLDSTATE_TEROKKAR_PVP_CAPTURE_BAR_DISPLAY, 0);
						StoredPlayers.erase(it2);
					}
					else
					{
						(*itr)->SendWorldStateUpdate(WORLDSTATE_TEROKKAR_PVP_CAPTURE_BAR_VALUE, Status);
						it2->second = timeptr;
						if(is_valid)
							plrcounts[(*itr)->GetTeam()]++;
					}
				}
			}

			// handle stuff for the last tick
			if(Status == 100 && m_bannerStatus != BANNER_STATUS_ALLIANCE)
			{
				m_bannerStatus = BANNER_STATUS_ALLIANCE;
				SetArtKit();

				// send message to everyone in the zone, has been captured by the Alliance
				_gameobject->GetMapMgr()->SendPvPCaptureMessage(ZONE_TEROKKAR_FOREST, ZONE_TEROKKAR_FOREST, "|cffffff00%s has been taken by the Alliance!|r", ControlPointName);

				// tower update
				TFg_allianceTowers++;
				UpdateTowerCount(_gameobject->GetMapMgr());

				// state update
				_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_neutralStateFields[towerid], 0);
				_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_allianceStateFields[towerid], 1);

				// woot
				TFg_towerOwners[towerid] = 1;
				UpdateInDB();
			}
			else if(Status == 0 && m_bannerStatus != BANNER_STATUS_HORDE)
			{
				m_bannerStatus = BANNER_STATUS_HORDE;
				SetArtKit();

				// send message to everyone in the zone, has been captured by the Horde
				_gameobject->GetMapMgr()->SendPvPCaptureMessage(ZONE_TEROKKAR_FOREST, ZONE_TEROKKAR_FOREST, "|cffffff00%s has been taken by the Horde!|r", ControlPointName);

				// tower update
				TFg_hordeTowers++;
				UpdateTowerCount(_gameobject->GetMapMgr());

				// state update
				_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_neutralStateFields[towerid], 0);
				_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_hordeStateFields[towerid], 1);

				// woot
				TFg_towerOwners[towerid] = 0;
				UpdateInDB();
			}
			else if(m_bannerStatus != BANNER_STATUS_NEUTRAL)
			{
				// if the difference for the faction is >50, change to neutral
				if(m_bannerStatus == BANNER_STATUS_ALLIANCE && Status <= 50)
				{
					// send message: The Alliance has lost control of point xxx
					m_bannerStatus = BANNER_STATUS_NEUTRAL;
					SetArtKit();

					TFg_allianceTowers--;
					UpdateTowerCount(_gameobject->GetMapMgr());

					_gameobject->GetMapMgr()->SendPvPCaptureMessage(ZONE_TEROKKAR_FOREST, ZONE_TEROKKAR_FOREST, "|cffffff00The Alliance have lost control of %s!|r", ControlPointName);

					// state update
					_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_allianceStateFields[towerid], 0);
					_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_neutralStateFields[towerid], 1);

					// woot
					TFg_towerOwners[towerid] = -1;
					UpdateInDB();
				}
				else if(m_bannerStatus == BANNER_STATUS_HORDE && Status >= 50)
				{
					// send message: The Alliance has lost control of point xxx
					m_bannerStatus = BANNER_STATUS_NEUTRAL;
					SetArtKit();

					TFg_hordeTowers--;
					UpdateTowerCount(_gameobject->GetMapMgr());

					_gameobject->GetMapMgr()->SendPvPCaptureMessage(ZONE_TEROKKAR_FOREST, ZONE_TEROKKAR_FOREST, "|cffffff00The Horde have lost control of %s!|r", ControlPointName);

					// state update
					_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_hordeStateFields[towerid], 0);
					_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_neutralStateFields[towerid], 1);

					// woot
					TFg_towerOwners[towerid] = -1;
					UpdateInDB();
				}
			}

			// send any out of range players the disable flag
			for(it2 = StoredPlayers.begin(); it2 != StoredPlayers.end();)
			{
				it3 = it2;
				++it2;

				if(it3->second != timeptr)
				{
					plr = _gameobject->GetMapMgr()->GetPlayer(it3->first);

					// they WILL be out of range at this point. this is guaranteed. means they left the set rly quickly.
					if(plr)
						plr->SendWorldStateUpdate(WORLDSTATE_TEROKKAR_PVP_CAPTURE_BAR_DISPLAY, 0);

					StoredPlayers.erase(it3);
				}
			}

			// work out current status for next tick
			uint32 delta;
			if(plrcounts[0] > plrcounts[1])
			{
				delta = plrcounts[0] - plrcounts[1];
				delta *= CAPTURE_RATE;

				// cap it at 25 so the banner always gets removed.
				if(delta > 25)
					delta = 25;

				Status += delta;
				if(Status >= 100)
					Status = 100;
			}
			else if(plrcounts[1] > plrcounts[0])
			{
				delta = plrcounts[1] - plrcounts[0];
				delta *= CAPTURE_RATE;

				// cap it at 25 so the banner always gets removed.
				if(delta > 25)
					delta = 25;

				if(delta > Status)
					Status = 0;
				else
					Status -= delta;
			}
		}

		void SetArtKit()
		{
			static const uint32 artkits_flagpole[3] = { 3, 2, 1 };
//		_gameobject->SetUInt32Value(GAMEOBJECT_ARTKIT, artkits_flagpole[m_bannerStatus]);
		}

		void OnSpawn()
		{
			m_bannerStatus = BANNER_STATUS_NEUTRAL;

			// preloaded data, do we have any?
			if(TFg_towerOwners[towerid] == 1)
			{
				m_bannerStatus = BANNER_STATUS_HORDE;
				Status = 0;

				// state update
				_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_hordeStateFields[towerid], 1);
				_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_neutralStateFields[towerid], 0);

				// countz
				TFg_hordeTowers++;
				UpdateTowerCount(_gameobject->GetMapMgr());
				SetArtKit();
			}
			else if(TFg_towerOwners[towerid] == 0)
			{
				m_bannerStatus = BANNER_STATUS_ALLIANCE;
				Status = 100;

				// state update
				_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_allianceStateFields[towerid], 1);
				_gameobject->GetMapMgr()->GetStateManager().UpdateWorldState(g_neutralStateFields[towerid], 0);

				// countz
				TFg_allianceTowers++;
				UpdateTowerCount(_gameobject->GetMapMgr());
				SetArtKit();
			}

			// start the event timer
			RegisterAIUpdateEvent(UPDATE_PERIOD);
		}

		//////////////////////////////////////////////////////////////////////////
		// Save Data To DB
		//////////////////////////////////////////////////////////////////////////
		void UpdateInDB()
		{
			static const char* fieldnames[TOWER_COUNT] = { "Terokkar-Tower1-status", "Terokkar-Tower2-status", "Terokkar-Tower3-status", "Terokkar-Tower4-status", "Terokkar-Tower5-status" };
			const char* msg = "-1";
			if(Status == 100)
				msg = "0";
			else
				msg = "1";

			WorldStateManager::SetPersistantSetting(fieldnames[towerid], msg);
		}
};

//////////////////////////////////////////////////////////////////////////
// Zone Hook
//////////////////////////////////////////////////////////////////////////

void TFZoneHook(PlayerPointer plr, uint32 Zone, uint32 OldZone)
{
	if(!plr)
		return;

	if(Zone == ZONE_TEROKKAR_FOREST)
	{
		if(TFg_superiorTeam == plr->GetTeam())
			plr->CastSpell(plr, dbcSpell.LookupEntry(BLESSING_OF_AUCHINDOUND), true);
	}
	else if(OldZone == ZONE_TEROKKAR_FOREST)
	{
		if(TFg_superiorTeam == plr->GetTeam())
			plr->RemovePositiveAura(BLESSING_OF_AUCHINDOUND);
	}
}

//////////////////////////////////////////////////////////////////////////
// Object Spawn Data
//////////////////////////////////////////////////////////////////////////

struct sgodata
{
	uint32 entry;
	float posx;
	float posy;
	float posz;
	float facing;
	float orientation[4];
	uint32 state;
	uint32 flags;
	uint32 faction;
	float scale;
	uint32 is_banner;
};

void TFSpawnObjects(shared_ptr<MapMgr> pmgr)
{
	if(!pmgr || pmgr->GetMapId() != 530)
		return;

	const static sgodata godata[] =
	{
		{ 183104, -3081.65f, 5335.03f, 17.1853f, -2.14675f, 0, 0, 0.878817f, -0.477159f, 1, 32, 0, 1 },
		{ 183411, -2939.9f, 4788.73f, 18.987f, 2.77507f, 0, 0, 0.983255f, 0.182236f, 1, 32, 0, 1 },
		{ 183412, -3174.94f, 4440.97f, 16.2281f, 1.86750f, 0, 0, 0.803857f, 0.594823f, 1, 32, 0, 1 },
		{ 183413, -3603.31f, 4529.15f, 20.9077f, 0.994838f, 0, 0, 0.477159f, 0.878817f, 1, 32, 0, 1 },
		{ 183414, -3812.37f, 4899.3f, 17.7249f, 0.087266f, 0, 0, 0.043619f, 0.999048f, 1, 32, 0, 1 },
		{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
	};

	uint32 i;
	const sgodata* p;
	for(i = 0; i < 5; ++i)
	{
		p = &godata[i];

		GameObjectPointer pGo = NULLGOB;
		pGo = pmgr->GetInterface()->SpawnGameObject(p->entry, p->posx, p->posy, p->posz, p->facing, false, 0, 0);
		if(!pGo)
			continue;

		pGo->SetByte(GAMEOBJECT_BYTES_1, GAMEOBJECT_BYTES_STATE, p->state);
		pGo->SetUInt32Value(GAMEOBJECT_FLAGS, p->flags);
		pGo->SetUInt32Value(GAMEOBJECT_FACTION, p->faction);

		for(uint32 j = 0; j < 4; ++j)
		{
			pGo->SetFloatValue(GAMEOBJECT_ROTATION + j, p->orientation[j]);
		}

		// now make his script
		pGo->SetScript(new TerokkarForestBannerAI(pGo));

		pGo->PushToWorld(pmgr);

		pGo->GetScript()->OnSpawn();
	}
}

void SetupPvPTerokkarForest(ScriptMgr* mgr)
{
	// register instance hooker
	mgr->register_hook(SERVER_HOOK_EVENT_ON_ZONE, (void*)&TFZoneHook);
	mgr->register_hook(SERVER_HOOK_EVENT_ON_CONTINENT_CREATE, (void*)&TFSpawnObjects);

	// load data
	const string Tower1 = WorldStateManager::GetPersistantSetting("Terokkar-Tower1-status", "-1");
	const string Tower2 = WorldStateManager::GetPersistantSetting("Terokkar-Tower2-status", "-1");
	const string Tower3 = WorldStateManager::GetPersistantSetting("Terokkar-Tower3-status", "-1");
	const string Tower4 = WorldStateManager::GetPersistantSetting("Terokkar-Tower4-status", "-1");
	const string Tower5 = WorldStateManager::GetPersistantSetting("Terokkar-Tower5-status", "-1");

	TFg_towerOwners[TOWER_1] = atoi(Tower1.c_str());
	TFg_towerOwners[TOWER_2] = atoi(Tower2.c_str());
	TFg_towerOwners[TOWER_3] = atoi(Tower3.c_str());
	TFg_towerOwners[TOWER_4] = atoi(Tower4.c_str());
	TFg_towerOwners[TOWER_5] = atoi(Tower5.c_str());
}


